/*
 * Copyright (c) 2024. hallele, Inc All rights reserved.
 */

package cn.hallele.ms.starter.web.framework;

import cn.hallele.infrastructure.metadata.constant.AppConst;
import cn.hallele.infrastructure.metadata.constant.MsgCodeConst;
import cn.hallele.infrastructure.metadata.exception.children.ActionException;
import cn.hallele.infrastructure.util.SpringUtil;
import cn.hallele.ms.starter.web.framework.context.ActionContext;
import cn.hallele.ms.starter.web.framework.service.ActionService;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * hallele_mc_core
 *
 * @author anle5
 * @since 2024/12/23 23:49
 */
@Slf4j
@Component
public class ActionExecutor {
    private static final String FRAMEWORK_CHAIN_NAME = "action";
    private static final FlowExecutor EXECUTOR = SpringUtil.getBean(FlowExecutor.class);

    /**
     * 调用一个流程并返回LiteFlowResponse，上下文为默认的DefaultContext，初始参数为null
     *
     * @param chainId 业务chainId
     * @param param   参数
     * @param context 上下文
     * @return LiteFlowResponse
     */
    public LiteflowResponse execute2RespNoException(String chainId, Object param, ActionContext context) {
        context.setSync(true);
        initFrameworkAction(chainId, param, context);
        return EXECUTOR.execute2RespWithRid(FRAMEWORK_CHAIN_NAME, param, MDC.get(AppConst.TRANCE_ID), context);
    }

    /**
     * 调用一个流程并返回LiteFlowResponse，上下文为默认的DefaultContext，初始参数为null
     *
     * @param chainId 业务chainId
     * @param param   参数
     * @param context 上下文
     * @return LiteFlowResponse
     */
    public Future<LiteflowResponse> execute2FutureNoException(String chainId, Object param, ActionContext context) {
        context.setSync(false);
        initFrameworkAction(chainId, param, context);
        return EXECUTOR.execute2FutureWithRid(FRAMEWORK_CHAIN_NAME, param, MDC.get(AppConst.TRANCE_ID), context);
    }

    /**
     * 调用一个流程并返回LiteFlowResponse，上下文为默认的DefaultContext，初始参数为null
     *
     * @param chainId 业务chainId
     * @param param   参数
     * @param context 上下文
     */
    public void execute2Resp(String chainId, Object param, ActionContext context) {
        LiteflowResponse response = execute2RespNoException(chainId, param, context);
        processResponse(response, context.getChainResponse());
    }

    /**
     * 调用一个流程并返回LiteFlowResponse，上下文为默认的DefaultContext，初始参数为null
     *
     * @param chainId 业务chainId
     * @param param   参数
     * @param context 上下文
     */
    public void execute2Future(String chainId, Object param, ActionContext context) {
        LiteflowResponse response = null;
        try {
            response = execute2FutureNoException(chainId, param, context).get();
            processResponse(response, context.getChainResponse());
        } catch (InterruptedException | ExecutionException e) {
            log.error("Action flow[Future] exec exception", e);
            throw new ActionException(MsgCodeConst.FAILED, e.getMessage());
        }
    }

    private void initFrameworkAction(String chainId, Object param, ActionContext context) {
        // 需要application层的service bean id保持和chain id一直
        context.setChainId(chainId);
        ActionService actionService = SpringUtil.getBean(context.getChainId());
        context.setActionService(actionService);
    }

    private void processResponse(LiteflowResponse response, LiteflowResponse chainResponse) {
        if (!response.isSuccess()) {
            Exception cause = response.getCause();
            log.error("Action flow exec exception", cause);

            if (cause instanceof RuntimeException runtimeException) {
                throw runtimeException;
            }
            throw new ActionException(MsgCodeConst.FAILED, cause.getMessage());
        }

        if (!chainResponse.isSuccess()) {
            Exception cause = chainResponse.getCause();
            log.error("Action chain exec exception", cause);

            if (cause instanceof RuntimeException runtimeException) {
                throw runtimeException;
            }
            throw new ActionException(MsgCodeConst.FAILED, cause.getMessage());
        }
    }
}
